/* Copyright (c) 2024 Qualcomm Technologies International, Ltd. */
/* %%version */

#include "gatt_ots_client_common_util.h"
#include "gatt_ots_client_read_object.h"
#include "gatt_ots_client_abort.h"

/***************************************************************************
NAME
    GattOtscAbortReq

DESCRIPTION
    API to abort current read/write ongoing procedure.
*/
bool GattOtscAbortReq(ServiceHandle svcHandle)
{
    GOTSC *gattOtsClient = ServiceHandleGetInstanceData(svcHandle);

    GATT_OTS_CLIENT_INFO("GOTSC : GattOtscAbortReq: svcHandle[%x]\n", svcHandle);

    if (gattOtsClient)
    {
        if (gattOtsClient->pendingCmd & OTSC_PENDING_OP_READ_OBJ_CONTENT)
        {
            OtscInternalMsgAbort *message = CsrPmemZalloc(sizeof(*message));

            message->svcHandle = gattOtsClient->srvcElem->service_handle;

            gattOtsClient->pendingCmd |= OTSC_PENDING_OP_ABORT_PROCEDURE;

            GattOtscMessageSend(gattOtsClient->libTask,
                                OTSC_INTERNAL_MSG_ABORT_REQ,
                                message);
            return (TRUE);
        }
        else
        {
            GATT_OTS_CLIENT_ERROR("GOTSC : Nothing to abort !\n");
            return (FALSE);
        }
    }
    else
    {
        GATT_OTS_CLIENT_ERROR("GOTSC : Invalid instance !\n");
        return (FALSE);
    }
}

/* From the OTP spec : 4.5.4.2 Read Object Content >> 
 * If the Object Server closes the Object Transfer Channel or 
 * if no object data is received via the Object Transfer Channel or
 * the object data ceases to be received for a continuous period exceeding 30 seconds (or less if a shorter timeout is specified by a higher layer specification),
 * and fewer than the expected number of octets have been received, 
 * the Object Client shall Object Transfer Profile.
 * Consider the object transfer to have failed and shall write the OACP Abort Op
 * Code to the OACP to ensure that the Object Server also considers the transfer to * be aborted.
 */
void OtscSendAbortCommand(GOTSC *const gattOtsClient)
{
    uint8  valueSize = sizeof(uint8);
    uint8* value     = CsrPmemZalloc(valueSize);

    value[0]  = GATT_OTSC_OBJ_ACTION_OP_ABORT;

    CsrBtGattWriteReqSend(gattOtsClient->srvcElem->gattId,
                          gattOtsClient->srvcElem->cid,
                          gattOtsClient->deviceData.objActionControlPointHandle,
                          0,
                          valueSize,
                          value);
}

void OtscSendAbortCfm(GOTSC *const    gattOtsClient,
                      CsrBtResultCode result,
                      CsrBtSupplier   supplier)
{
    GattOtscAbortCfm *message = CsrPmemZalloc(sizeof(*message));

    message->svcHandle  = gattOtsClient->srvcElem->service_handle;
    message->resultCode = result;
    message->supplier   = supplier;

    /* Send the confirmation message to app task  */
    GattOtscMessageSend(gattOtsClient->appTask, GATT_OTSC_ABORT_CFM, message);

    /* Reset the pending cmd flag */
    gattOtsClient->pendingCmd &= ~OTSC_PENDING_OP_ABORT_PROCEDURE;

    if (result == GATT_OTSC_RESULT_CODE_SUCCESS &&
        gattOtsClient->btConnId != CSR_BT_CONN_ID_INVALID)
    {
        /* Disconnect the L2CAP connections */
        CsrBtCml2caDisconnectReqSend(gattOtsClient->btConnId);
    }

    GATT_OTS_CLIENT_INFO("GOTSC: OtscSendAbortCfm: svcHandle(0x%02x)\n", gattOtsClient->srvcElem->service_handle);
}

void OtscHandleInternalMsgAbort(GOTSC *gattOtsClient, OtscInternalMsgAbort* msg)
{
    /* Abort if read object's content is still in progress */
    if (gattOtsClient->pendingCmd & OTSC_PENDING_OP_READ_OBJ_CONTENT)
    {
        /* Send read object procedure aborted message to application */
        OtscSendReadObjectCfm(gattOtsClient, 0, NULL, FALSE,
                              GATT_OTSC_RESULT_CODE_OPERATION_ABORTED,
                              GATT_SUPPLIER_OTS_CLIENT);
        /* Send abort command to remote */
        OtscSendAbortCommand(gattOtsClient);
    }
    else
    {
        /* Inform app about the failure */
        OtscSendAbortCfm(gattOtsClient,
                         GATT_OTSC_RESULT_CODE_NOTHING_TO_ABORT,
                         GATT_SUPPLIER_OTS_CLIENT);
    }
    CSR_UNUSED(msg);
}

void OtscHandleWriteCfmToAbort(GOTSC *const gattOtsClient,
                               const CsrBtGattWriteCfm *cfm)
{
    if (cfm->resultCode == CSR_BT_GATT_RESULT_SUCCESS &&
        cfm->resultSupplier == CSR_BT_SUPPLIER_GATT)
    {
        /* Start OACP timer and wait for OACP indication */
        OtscStartOacpTimer(gattOtsClient);
    }
    else
    {
        /* Inform app about the failure */
        OtscSendAbortCfm(gattOtsClient, cfm->resultCode, cfm->resultSupplier);
    }
}

